package gameEngine.agents;

import java.util.ArrayList;
import gameEngine.agents.choosingStrategy.ChoosingStrategy;
import gameEngine.agents.choosingStrategy.StrategyFactory;
import gameEngine.agents.choosingStrategy.representation.StrategyRepresentation;
import gameEngine.agents.distrebution.Distribution;
import gameEngine.agents.distrebution.DistributionFactory;
import gameEngine.agents.distrebution.FixedDistribution;
import gameEngine.agents.distrebution.Normal;
import gameEngine.agents.distrebution.representation.DistributionRepresentation;
import gameEngine.agents.observationSystem.analyzer.AnalayzerFactory;
import gameEngine.agents.observationSystem.analyzer.ObservationAnalyzer;
import gameEngine.agents.observationSystem.analyzer.EnumOfAnalyzer.Analyzer_Type;
import gameEngine.agents.observationSystem.samples.Sample;
import gameEngine.agents.teamMateModel.TeamMateModel;
import gameEngine.agents.teamMateModel.TeamMateModelFactory;
import gameEngine.agents.teamMateModel.EnumOfTeamateModel.Teammate_Meodel_Type;

/**
 *	@author vladimir && Alon
 *
 *	epsilonGreedy - rate of the exploration
 *	observations - collection of observations, the size of the Collection
 *				 should be, as the size of number  of agents
 *	distribution - It is the competence of the player 
 */
public class BasicAgent implements Agent {

	private int ID;
	private double epsilonGreedy;
	private double competence;
	private ObservationAnalyzer observationAnalyzer;
	private TeamMateModel teamMateModel;
	private ChoosingStrategy choosingStrategy;
	private Distribution distribution;
	
	/**
	 * 
	 * @param iD
	 * @param epsilonGreedy
	 * @param competence
	 * @param observationAnalyzer
	 * @param choosingStrategy
	 * @param teamMateModel
	 * @param distribution
	 */
	public BasicAgent(int iD, double epsilonGreedy, double competence,
			ObservationAnalyzer observationAnalyzer,
			ChoosingStrategy choosingStrategy,
			TeamMateModel teamMateModel,
			Distribution distribution) {
		super();
		ID = iD;
		this.epsilonGreedy = epsilonGreedy;
		this.competence = competence;
		this.observationAnalyzer = observationAnalyzer;
		this.choosingStrategy = choosingStrategy;
		this.teamMateModel = teamMateModel;
		this.distribution = distribution;
	}
	
	/**
	 * Contractor with simple members only, use init method then to activate factories 
	 * @param iD
	 * @param epsilonGreedy
	 * @param competence
	 */
	public BasicAgent(int iD,double epsilonGreedy,double competence){
		super();
		ID=iD;
		this.epsilonGreedy = epsilonGreedy;
		this.competence = competence;
	}
	
	/**
	 * init factories
	 * @param analayzerType
	 * @param strategyType
	 * @param teamMateType
	 * @param distribution
	 * @param agents
	 */
	public void init(Analyzer_Type analayzerType,StrategyRepresentation strRepresentation,
			Teammate_Meodel_Type teamMateType,
			DistributionRepresentation diRepresentation ,ArrayList<Agent> agents )
	{
		
		this.teamMateModel = TeamMateModelFactory.create(teamMateType, ID, agents);
		this.observationAnalyzer = AnalayzerFactory.create(analayzerType, teamMateModel);
		this.choosingStrategy = StrategyFactory.create(strRepresentation);
		this.distribution = DistributionFactory.create(diRepresentation, competence);
	}

	/**
	 *  return the outcome of the current action 
	 */
	public double play() {
		return distribution.generateValue();
	}
	
	public int chooseNextPlayer() {
		return choosingStrategy.chooseNextPlayer(teamMateModel);
	}
	/**
	 * return epsilon greedy
	 * @return
	 */
	public double getEpsilonGreedy() {
		return epsilonGreedy;
	}
	/**
	 * set epsilon greedy
	 * @param epsilonGreedy
	 */
	public void setEpsilonGreedy(double epsilonGreedy) {
		this.epsilonGreedy = epsilonGreedy;
	}
	/**
	 * return agents competence
	 * @return double competence
	 */
	public double getCompitance() {
		return competence;
	}
	/**
	 * set agents competence
	 * @param competence
	 */
	public void setCompetance(double compitance) {
		this.competence = compitance;
	}
	/**
	 * set agents id
	 * @param iD
	 */
	public void setID(int iD) {
		ID = iD;
	}

	/**
	 * returns agents id
	 */
	public int getID() {
		return ID;
	}

	public void analyzeSample(Sample s) {
		this.observationAnalyzer.analyze(s);
	}

	public double getCompetence() {
		return competence;
	}

	public void setCompetence(double competence) {
		this.competence = competence;
	}

	public TeamMateModel getTeamMateModel() {
		return teamMateModel;
	}

	public void setTeamMateModel(TeamMateModel teamMateModel) {
		this.teamMateModel = teamMateModel;
	}

	public ChoosingStrategy getChoosingStrategy() {
		return choosingStrategy;
	}

	public void setChoosingStrategy(ChoosingStrategy choosingStrategy) {
		this.choosingStrategy = choosingStrategy;
	}
	
	
	public ObservationAnalyzer getObservationAnalyzer() {
		return observationAnalyzer;
	}

	public void setObservationAnalyzer(ObservationAnalyzer observationAnalyzer) {
		this.observationAnalyzer = observationAnalyzer;
	}

	public Distribution getDistribution() {
		return distribution;
	}

	public void setDistribution(Distribution distribution) {
		this.distribution = distribution;
	}
	
	/**
	 * clone the agent
	 */
	@Override
	public BasicAgent clone(){
		BasicAgent clone=new BasicAgent(this.ID, this.epsilonGreedy, this.competence);
		TeamMateModel model=teamMateModel.clone();
		clone.setTeamMateModel(model);
		clone.setObservationAnalyzer(observationAnalyzer.clone());
		clone.setChoosingStrategy(choosingStrategy.clone());
		clone.setDistribution(distribution.clone());
		return clone;
	}
	
	/**
	 * to be used only on an Agent clone!!!!
	 * we need to set the new model after cloning all agents
	 * as some might have different properties values 
	 * @param agents the new cloned list
	 */
	public void initClonesModel(ArrayList<Agent> agents){
		getTeamMateModel().init(agents);
		setObservationAnalyzer(getObservationAnalyzer().initAnalyzer(getTeamMateModel()));
		if (getDistribution() instanceof FixedDistribution) {
			((FixedDistribution)getDistribution()).setCompetence(getCompetence());
			
		}
		if (getDistribution() instanceof Normal) {
			setDistribution(new Normal(getCompetence(),getDistribution().getVariance()));			
		}
	}


}
